#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

#include "ssd1306.h"
//#include "font8x8_basic.h"

/*
 You have to set this config value with menuconfig
 CONFIG_INTERFACE

 for i2c
 CONFIG_MODEL
 CONFIG_SDA_GPIO
 CONFIG_SCL_GPIO
 CONFIG_RESET_GPIO

 for SPI
 CONFIG_CS_GPIO
 CONFIG_DC_GPIO
 CONFIG_RESET_GPIO
*/

#define TAG "SSD1306"

// https://www.instructables.com/How-to-use-OLED-display-arduino-module/
// 'bentlay', 128x64px
uint8_t bentlay[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x60, 0x0c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xc0, 0x80, 0x00, 0x20, 0xf0, 0x00, 0x36, 0x3f, 0xf8, 0x98, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x03, 
0x9f, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xe3, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xf9, 
0x9f, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xf3, 
0xc7, 0xff, 0xff, 0xff, 0x18, 0x07, 0xfd, 0xff, 0xff, 0xbf, 0xd0, 0x18, 0xff, 0xff, 0xff, 0xe7, 
0xf0, 0x00, 0x00, 0x00, 0x79, 0xfe, 0x7f, 0xff, 0xff, 0xbe, 0x7f, 0x9e, 0x00, 0x00, 0x00, 0x0f, 
0xf3, 0xff, 0xff, 0xff, 0xbd, 0xff, 0xbb, 0xc0, 0x0f, 0xdd, 0xff, 0xbd, 0xff, 0xff, 0xff, 0xcf, 
0xf9, 0xff, 0xff, 0xf8, 0x38, 0xff, 0xae, 0x00, 0x00, 0xed, 0xff, 0x1c, 0x3f, 0xff, 0xff, 0x9f, 
0xfc, 0x7f, 0x80, 0x0f, 0x9e, 0x7f, 0xd8, 0x00, 0x00, 0x35, 0xfe, 0x7b, 0xe0, 0x01, 0xfe, 0x3f, 
0xfe, 0x00, 0x3f, 0xff, 0x0f, 0x3f, 0x50, 0x0f, 0xc0, 0x1a, 0xfc, 0xf0, 0xff, 0xfc, 0x00, 0xff, 
0xff, 0x3f, 0xff, 0xc1, 0xee, 0x9e, 0xa0, 0x7f, 0xf8, 0x0e, 0xf3, 0x77, 0x07, 0xff, 0xfc, 0xff, 
0xff, 0x9f, 0xe0, 0x3f, 0xc3, 0x84, 0x80, 0xfe, 0xfc, 0x05, 0x61, 0xc7, 0xf8, 0x0f, 0xf1, 0xff, 
0xff, 0xe0, 0x0f, 0xfe, 0x3a, 0xc0, 0x80, 0xf8, 0x3e, 0x01, 0x07, 0x58, 0x7f, 0xe0, 0x07, 0xff, 
0xff, 0xf3, 0xff, 0xe1, 0xf1, 0xf4, 0x00, 0xf8, 0x3e, 0x00, 0x6f, 0x1f, 0x87, 0xff, 0x8f, 0xff, 
0xff, 0xf8, 0xfc, 0x1f, 0xce, 0x6f, 0x00, 0xf8, 0x3e, 0x00, 0xb4, 0x63, 0xf8, 0x3f, 0x1f, 0xff, 
0xff, 0xfe, 0x01, 0xfe, 0x3e, 0x1a, 0x00, 0xf8, 0x3c, 0x00, 0xd8, 0x7c, 0xff, 0x80, 0x7f, 0xff, 
0xff, 0xff, 0x8f, 0xf8, 0xf9, 0x82, 0x00, 0xfc, 0x78, 0x00, 0x43, 0xbf, 0x1f, 0xf3, 0xff, 0xff, 
0xff, 0xff, 0xc3, 0x87, 0xe7, 0x20, 0x00, 0xff, 0xf0, 0x00, 0x0d, 0xe7, 0xe1, 0xc3, 0xff, 0xff, 
0xff, 0xff, 0xf8, 0x3f, 0x9e, 0x64, 0x00, 0xff, 0xfc, 0x00, 0x2e, 0x79, 0xfc, 0x1f, 0xff, 0xff, 
0xff, 0xff, 0xfe, 0x7c, 0x79, 0xcc, 0x00, 0xfc, 0x3e, 0x00, 0x37, 0x3e, 0x3c, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0x81, 0xe3, 0x9a, 0x00, 0xf8, 0x3e, 0x00, 0x9b, 0xcf, 0x81, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xfc, 0x06, 0x36, 0x00, 0xf8, 0x1f, 0x00, 0xcc, 0xe0, 0x3f, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xf0, 0x06, 0x00, 0xf8, 0x1f, 0x00, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0x98, 0x00, 0xfc, 0x3e, 0x00, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x80, 0xfe, 0xfe, 0x03, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xc0, 0x7f, 0xfc, 0x07, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xd7, 0xe0, 0x0f, 0xe0, 0x0f, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xf0, 0x00, 0x00, 0x1f, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xfc, 0x00, 0x00, 0x7f, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, 0x00, 0x01, 0xff, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xfe, 0xd4, 0x56, 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x3e, 0xb5, 0x5a, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5d, 0xad, 0x7b, 0x75, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, 0x6d, 0x6f, 0x8b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0x6d, 0x6d, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x6f, 0x6d, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x5f, 0x64, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x0b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

uint8_t RotateByte(uint8_t ch1) {
	uint8_t ch2 = 0;
	int j;
	for (j=0;j<8;j++) {
		ch2 = (ch2 << 1) + (ch1 & 0x01);
		ch1 = ch1 >> 1;
	}
	return ch2;
}

void app_main(void)
{
	SSD1306_t dev;

#if CONFIG_I2C_INTERFACE
	ESP_LOGI(TAG, "INTERFACE is i2c");
	ESP_LOGI(TAG, "CONFIG_SDA_GPIO=%d",CONFIG_SDA_GPIO);
	ESP_LOGI(TAG, "CONFIG_SCL_GPIO=%d",CONFIG_SCL_GPIO);
	ESP_LOGI(TAG, "CONFIG_RESET_GPIO=%d",CONFIG_RESET_GPIO);
	i2c_master_init(&dev, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);
#endif // CONFIG_I2C_INTERFACE

#if CONFIG_SPI_INTERFACE
	ESP_LOGI(TAG, "INTERFACE is SPI");
	ESP_LOGI(TAG, "CONFIG_MOSI_GPIO=%d",CONFIG_MOSI_GPIO);
	ESP_LOGI(TAG, "CONFIG_SCLK_GPIO=%d",CONFIG_SCLK_GPIO);
	ESP_LOGI(TAG, "CONFIG_CS_GPIO=%d",CONFIG_CS_GPIO);
	ESP_LOGI(TAG, "CONFIG_DC_GPIO=%d",CONFIG_DC_GPIO);
	ESP_LOGI(TAG, "CONFIG_RESET_GPIO=%d",CONFIG_RESET_GPIO);
	spi_master_init(&dev, CONFIG_MOSI_GPIO, CONFIG_SCLK_GPIO, CONFIG_CS_GPIO, CONFIG_DC_GPIO, CONFIG_RESET_GPIO);
#endif // CONFIG_SPI_INTERFACE

#if CONFIG_FLIP
	dev._flip = true;
	ESP_LOGW(TAG, "Flip upside down");
#endif

#if CONFIG_SSD1306_128x64
	ESP_LOGI(TAG, "Panel is 128x64");
	ssd1306_init(&dev, 128, 64);
#endif // CONFIG_SSD1306_128x64
#if CONFIG_SSD1306_128x32
	ESP_LOGE(TAG, "Panel is 128x32. This demo cannot be run.");
	while(1) { vTaskDelay(1); }
#endif // CONFIG_SSD1306_128x32

	// Allocate memory for work frame
	uint8_t *buffer1 = (uint8_t *)malloc(1024);
	if (buffer1 == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	uint8_t *buffer2 = (uint8_t *)malloc(1024);
	if (buffer2 == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	uint8_t *buffer3 = (uint8_t *)malloc(1024);
	if (buffer3 == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	uint8_t *buffer4 = (uint8_t *)malloc(1024);
	if (buffer4 == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	ssd1306_contrast(&dev, 0xff);
	ssd1306_clear_screen(&dev, false);
	ssd1306_bitmaps(&dev, 0, 0, bentlay, 128, 64, false);
	vTaskDelay(2000 / portTICK_PERIOD_MS);

#if 0
	// 0b00010011 --> 0b11001000
	uint8_t ch1 = 0x13;
	uint8_t ch2 = RotateByte(ch1);
	ESP_LOGI(TAG, "ch1=0x%x ch2=0x%x", ch1, ch2);
#endif

	// generate inverted image
	for (int page=0;page<8;page++) {
		int index1 = page * 128;
		ssd1306_get_page(&dev, page, &buffer1[index1]);
		for (int seg=0;seg<128;seg++) {
			buffer2[index1+seg] = ~buffer1[index1+seg];
		}
		vTaskDelay(1);
	}

	// generate fliped image
	for (int page=0;page<8;page++) {
		int index1 = page * 128;
		int index2 = index1 + 127;
		ssd1306_get_page(&dev, page, &buffer1[index1]);
		for (int seg=0;seg<64;seg++) {
			ESP_LOGD(TAG, "index1=%d index2=%d", index1, index2);
			uint8_t wk1 = buffer1[index1];
			uint8_t wk2 = buffer1[index2];
			wk1 = RotateByte(wk1);
			wk2 = RotateByte(wk2);
			buffer3[index1++] = wk2;
			buffer3[index2--] = wk1;
		}
		vTaskDelay(1);
	}

	// generate fliped and inverted image
	for (int page=0;page<8;page++) {
		int index1 = page * 128;
		for (int seg=0;seg<128;seg++) {
			buffer4[index1+seg] = ~buffer3[index1+seg];
		}
		vTaskDelay(1);
	}

	while(1) {
		for (int page=0;page<8;page++) {
			int index = page * 128;
			ssd1306_set_page(&dev, page, &buffer1[index]);
			ssd1306_show_buffer(&dev);
			vTaskDelay(5);
		}
		vTaskDelay(2000 / portTICK_PERIOD_MS);

		//for (int page=0;page<8;page++) {
		for (int page=0;page<4;page++) {
			int index = page * 128;
			ssd1306_set_page(&dev, page, &buffer2[index]);
			ssd1306_show_buffer(&dev);
			vTaskDelay(5);
		}
		vTaskDelay(2000 / portTICK_PERIOD_MS);

		//for (int page=7;page>=0;page--) {
		for (int page=7;page>=4;page--) {
			int index = (7-page)*128;
			ssd1306_set_page(&dev, page, &buffer3[index]);
			ssd1306_show_buffer(&dev);
			vTaskDelay(5);
		}
		vTaskDelay(2000 / portTICK_PERIOD_MS);

		//for (int page=7;page>=0;page--) {
		for (int page=3;page>=0;page--) {
			int index = (7-page)*128;
			ssd1306_set_page(&dev, page, &buffer4[index]);
			ssd1306_show_buffer(&dev);
			vTaskDelay(5);
		}
		vTaskDelay(2000 / portTICK_PERIOD_MS);

		for (int page=7;page>=4;page--) {
			int index = (7-page)*128;
			ssd1306_set_page(&dev, page, &buffer4[index]);
			ssd1306_show_buffer(&dev);
			vTaskDelay(5);
		}
		vTaskDelay(2000 / portTICK_PERIOD_MS);
	}
}
